/*  PLUTO JS -Scripting Web Server.
    Copyright (C) 2013 vivek gangadharan

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
package pluto.core;

import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import org.jgroups.JChannel;
import org.mozilla.javascript.Function;
import static pluto.core.AppLogger.*;

public class ApplicationConfiguration {

    private ConcurrentHashMap<ConfigurationParameter, Object> configMap;
    private ConcurrentHashMap<String, Function> urlMap;
    public static final String LoggerName = "pluto";
    private static String logPattern = File.separatorChar + "app.log";
    public final static int SocketBlackLog = 6;
    public final static int DEFAULT_SYNCRATE = 10000;
    public final static int DEFAULT_REQUEST_HANDLER_POOL_SIZE = 25;
    public final static int DEFAULT_PORT = 8013;

    public ApplicationConfiguration() throws Exception {
        configMap = new ConcurrentHashMap<>();
        urlMap = new ConcurrentHashMap<>();
        configMap.put(ConfigurationParameter.cluster, new JChannel());
    }

    public void app(String filePath) {
        if (filePath == null) {
            return;
        }
        File file = new File(filePath);
        app(file);
    }

    public void app(File file) {
        if (file == null) {
            return;
        }
        if (file.exists() && file.isFile()) {
            configMap.put(ConfigurationParameter.app, file);
        } else {
            log.log(Level.SEVERE, Localizer.getMessage(Message.AppFileNotFound, file.getAbsolutePath()));
            throw new IllegalArgumentException(Localizer.getMessage(Message.AppFileNotFound, file.getAbsolutePath()));
        }
    }

    public File app() {
        if (configMap.containsKey(ConfigurationParameter.app)) {
            return (File) configMap.get(ConfigurationParameter.app);
        }
        return null;
    }

    public void root(String filePath) {
        if (filePath == null) {
            return;
        }
        File file = new File(filePath);
        root(file);
    }

    public void root(File file) {
        if (file == null) {
            return;
        }
        if (file.exists() && file.isDirectory()) {
            configMap.put(ConfigurationParameter.root, file);
        } else {
            log.log(Level.SEVERE, Localizer.getMessage(Message.RootNotFound, file.getAbsolutePath()));
            throw new IllegalArgumentException(Localizer.getMessage(Message.RootNotFound, file.getAbsolutePath()));
        }
    }

    public File root() {
        if (configMap.containsKey(ConfigurationParameter.root)) {
            return (File) configMap.get(ConfigurationParameter.root);
        }
        return null;
    }

    public void host(String hostName) throws UnknownHostException {
        if (hostName == null) {
            return;
        }
        try {
            host(InetAddress.getByName(hostName));
        } catch (UnknownHostException ex) {
            log.log(Level.SEVERE, Localizer.getMessage(Message.HostNotFound, hostName));
            throw ex;
        }
    }

    public void host(InetAddress address) {
        if (address == null) {
            return;
        }
        configMap.put(ConfigurationParameter.host, address);
    }

    public InetAddress host() {
        if (configMap.containsKey(ConfigurationParameter.host)) {
            return (InetAddress) configMap.get(ConfigurationParameter.host);
        }
        return null;
    }

    public void port(String port) throws UnknownHostException {
        port(Integer.parseInt(port));
    }

    public void port(Integer port) {
        configMap.put(ConfigurationParameter.port, port);
    }

    public int port() {
        if (configMap.containsKey(ConfigurationParameter.port)) {
            return (Integer) configMap.get(ConfigurationParameter.port);
        }
        return DEFAULT_PORT;
    }

    public void loglevel(String loglevel) throws IOException {
        if (loglevel == null) {
            return;
        }
        log.setLevel(Level.parse(loglevel));
    }

    public void log(String filePath) throws IOException {
        if (filePath == null) {
            return;
        }

        File file = new File(filePath);
        log(file);
    }

    public void log(File file) throws IOException {
        if (file == null) {
            return;
        }
        if (file.exists() && file.isDirectory()) {
            try 
            {
                FileHandler fileHandler = new FileHandler(file.getAbsolutePath() + logPattern, true);
                fileHandler.setFormatter(new LogFormatter());
                log.addHandler(fileHandler);
                configMap.put(ConfigurationParameter.log, file);
            } catch (IOException ex) {
                log.log(Level.SEVERE, Localizer.getMessage(Message.ErrorConfigLog, file.getAbsolutePath()), ex);
                throw ex;
            } catch (SecurityException ex) {
                log.log(Level.SEVERE, Localizer.getMessage(Message.ErrorConfigLog, file.getAbsolutePath()), ex);
                throw ex;
            }
        } else {
            log.log(Level.SEVERE, Localizer.getMessage(Message.LogFileNotFound, file.getAbsolutePath()));
            throw new IllegalArgumentException(Localizer.getMessage(Message.LogFileNotFound, file.getAbsolutePath()));
        }
    }

    public void web(String filePath) {
        if (filePath == null) {
            return;
        }
        File file = new File(filePath);
        web(file);
    }

    public void web(File file) {
        if (file == null) {
            return;
        }
        if (file.exists() && file.isDirectory()) {
            configMap.put(ConfigurationParameter.web, file);
        } else {
            log.log(Level.SEVERE, Localizer.getMessage(Message.WebFileNotFound, file.getAbsolutePath()));
            throw new IllegalArgumentException(Localizer.getMessage(Message.WebFileNotFound, file.getAbsolutePath()));
        }
    }

    public File web() {
        if (configMap.containsKey(ConfigurationParameter.web)) {
            return (File) configMap.get(ConfigurationParameter.web);
        }
        return null;
    }

    public void jss(String filePath) {
        if (filePath == null) {
            return;
        }
        File file = new File(filePath);
        jss(file);
    }

    public void jss(File file) {
        if (file == null) {
            return;
        }
        if (file.exists() && file.isDirectory()) {
            configMap.put(ConfigurationParameter.jss, file);
        } else {
            log.log(Level.SEVERE, Localizer.getMessage(Message.JSSFileNotFound, file.getAbsolutePath()));
            throw new IllegalArgumentException(Localizer.getMessage(Message.JSSFileNotFound, file.getAbsolutePath()));
        }
    }

    public File jss() {
        if (configMap.containsKey(ConfigurationParameter.jss)) {
            return (File) configMap.get(ConfigurationParameter.jss);
        }
        return null;
    }

    public File jssFile(String urlPattern) {
        if (urlPattern == null || !urlPattern.endsWith(".jss") || !configMap.containsKey(ConfigurationParameter.jss)) {
            return null;
        }

        File scriplet = new File(this.jss(), urlPattern);
        if (scriplet.exists() && scriplet.isFile()) {
            return scriplet;
        }
        return null;
    }

    public void lib(String filePath) {
        if (filePath == null) {
            return;
        }
        File file = new File(filePath);
        web(file);
    }

    public void lib(File file) throws MalformedURLException {
        if (file == null) {
            return;
        }

        if (file.exists() && file.isDirectory()) {
            ClassLoader classLoader = null;
            List<URL> urls = new ArrayList<>();
            if (file.listFiles() != null) {
                for (File f : file.listFiles()) {
                    if (f.isFile() && f.getName().endsWith(".jar")) {
                        try {
                            urls.add(f.toURI().toURL());
                        } catch (MalformedURLException m) {
                            log.log(Level.SEVERE, Localizer.getMessage(Message.ErrorLoadingJarFile, f.getAbsolutePath()), m);
                            throw m;
                        }

                    }
                }
            }
            if (!urls.isEmpty()) {
                URL[] uls = new URL[urls.size()];
                int i = 0;
                for (URL u : urls) {
                    uls[i++] = u;
                }
                classLoader = new URLClassLoader(uls);
            }
            configMap.put(ConfigurationParameter.lib, classLoader);
        } else {
            log.log(Level.SEVERE, Localizer.getMessage(Message.LibNotFound, file.getAbsolutePath()));
            throw new IllegalArgumentException(Localizer.getMessage(Message.LibNotFound, file.getAbsolutePath()));
        }
    }

    public ClassLoader lib() {
        if (configMap.containsKey(ConfigurationParameter.lib)) {
            return (ClassLoader) configMap.get(ConfigurationParameter.lib);
        }
        return null;
    }

    public void syncRate(String rate) {
        syncRate(Integer.parseInt(rate));
    }

    public void syncRate(int rate) {
        if (rate < DEFAULT_SYNCRATE) {
            return;
        }
        configMap.put(ConfigurationParameter.syncRate, rate);
    }

    public int syncRate() {
        if (configMap.containsKey(ConfigurationParameter.syncRate)) {
            return (int) configMap.get(ConfigurationParameter.syncRate);
        }
        return DEFAULT_SYNCRATE;
    }

    public void cluster(String clusterGroupName) {

        if (clusterGroupName == null) {
            return;
        }
        configMap.put(ConfigurationParameter.clusterGroupName, clusterGroupName);

    }

    public JChannel cluster() {
        if (configMap.containsKey(ConfigurationParameter.cluster)) {
            return (JChannel) configMap.get(ConfigurationParameter.cluster);
        }
        return null;
    }

    public void sslKeyStroke(String filePath) {
        if (filePath == null) {
            return;
        }
        File file = new File(filePath);
        sslKeyStroke(file);
    }

    public void sslKeyStroke(File file) {
        if (file == null) {
            return;
        }
        if (file.exists() && file.isFile()) {
            configMap.put(ConfigurationParameter.sslKeyStroke, file);
        } else {
            log.log(Level.SEVERE, Localizer.getMessage(Message.KSFileNotFound, file.getAbsolutePath()));
            throw new IllegalArgumentException(Localizer.getMessage(Message.KSFileNotFound, file.getAbsolutePath()));
        }
    }

    public File sslKeyStroke() {
        if (configMap.containsKey(ConfigurationParameter.sslKeyStroke)) {
            return (File) configMap.get(ConfigurationParameter.sslKeyStroke);
        }
        return null;
    }

    public void sslKeyStrokePassword(String password) {
        if (password == null) {
            return;
        }
        configMap.put(ConfigurationParameter.sslKeyPassword, password.toCharArray());
    }

    public char[] sslKeyStrokePassword() {
        if (configMap.containsKey(ConfigurationParameter.sslKeyPassword)) {
            return (char[]) configMap.get(ConfigurationParameter.sslKeyPassword);
        }
        return null;
    }

    public void sslStorePassword(String password) {
        if (password == null) {
            return;
        }
        configMap.put(ConfigurationParameter.sslStorePassword, password.toCharArray());
    }

    public char[] sslStorePassword() {
        if (configMap.containsKey(ConfigurationParameter.sslStorePassword)) {
            return (char[]) configMap.get(ConfigurationParameter.sslStorePassword);
        }
        return null;
    }

    public boolean has(ConfigurationParameter config) {
        return configMap.containsKey(config);
    }

    public void requestHandlerPool(String size) {
        requestHandlerPool(Integer.parseInt(size));
    }

    public void requestHandlerPool(int size) {
        if (size < 1) {
            return;
        }
        configMap.put(ConfigurationParameter.requestHandlerPool, size);
    }

    public int requestHandlerPool() {
        if (configMap.containsKey(ConfigurationParameter.requestHandlerPool)) {
            return (int) configMap.get(ConfigurationParameter.requestHandlerPool);
        }
        return DEFAULT_REQUEST_HANDLER_POOL_SIZE;
    }

    public void mapUrl(String pattern, Function function) {
        if (pattern == null && function == null) {
            return;
        }
        urlMap.put(pattern, function);
    }

    public Function getFunction(String pattern) {
        for (Entry<String, Function> entry : urlMap.entrySet()) {
            if (entry.getKey().matches(pattern)) {
                return entry.getValue();
            }
        }
        return null;
    }

    public void allowDirListing(boolean flag) {
        configMap.put(ConfigurationParameter.allowDirListing, flag);
    }

    public boolean allowDirListing() {
        if (configMap.containsKey(ConfigurationParameter.allowDirListing)) {
            return (boolean) configMap.get(ConfigurationParameter.allowDirListing);
        }
        return false;
    }
    
    public void connect( ) throws Exception{
        if(has(ConfigurationParameter.cluster) && has(ConfigurationParameter.clusterGroupName)){
            JChannel channel= (JChannel) configMap.get(ConfigurationParameter.cluster);
            channel.connect((String)configMap.get(ConfigurationParameter.clusterGroupName));
        }
    }
}